using System.Collections.Immutable;
using System.Net;
using JetBrains.Annotations;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Errors;
using JsonApiDotNetCore.Queries.Expressions;
using JsonApiDotNetCore.Resources.Annotations;
using JsonApiDotNetCore.Serialization.Objects;

namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading;

[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
// The constructor parameters will be resolved from the container, which means you can take on any dependency that is also defined in the container.
public sealed class PlanetDefinition(IResourceGraph resourceGraph, IClientSettingsProvider clientSettingsProvider, ResourceDefinitionHitCounter hitCounter)
    : HitCountingResourceDefinition<Planet, int>(resourceGraph, hitCounter)
{
    private readonly IClientSettingsProvider _clientSettingsProvider = clientSettingsProvider;

    protected override ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.Reading;

    public override IImmutableSet<IncludeElementExpression> OnApplyIncludes(IImmutableSet<IncludeElementExpression> existingIncludes)
    {
        base.OnApplyIncludes(existingIncludes);

        if (_clientSettingsProvider.IsIncludePlanetMoonsBlocked && existingIncludes.Any(include => include.Relationship.Property.Name == nameof(Planet.Moons)))
        {
            throw new JsonApiException(new ErrorObject(HttpStatusCode.BadRequest)
            {
                Title = "Including moons is not permitted."
            });
        }

        return existingIncludes;
    }

    public override FilterExpression? OnApplyFilter(FilterExpression? existingFilter)
    {
        base.OnApplyFilter(existingFilter);

        if (_clientSettingsProvider.ArePlanetsWithPrivateNameHidden)
        {
            AttrAttribute privateNameAttribute = ResourceType.GetAttributeByPropertyName(nameof(Planet.PrivateName));

            FilterExpression hasNoPrivateName = new ComparisonExpression(ComparisonOperator.Equals, new ResourceFieldChainExpression(privateNameAttribute),
                NullConstantExpression.Instance);

            return LogicalExpression.Compose(LogicalOperator.And, hasNoPrivateName, existingFilter);
        }

        return existingFilter;
    }
}
